/**
 * @desc 函数防抖 
 * 与throttle不同的是，debounce保证一个函数在多少毫秒内不再被触发，只会执行一次，
 * 要么在第一次调用return的防抖函数时执行，要么在延迟指定毫秒后调用。
 * @example 适用场景：如在线编辑的自动存储防抖。
 * @param  {Number}   delay         0或者更大的毫秒数。 对于事件回调，大约100或250毫秒（或更高）的延迟是最有用的。
 * @param  {Boolean}  atBegin       可选，默认为false。
 *                                  如果`atBegin`为false或未传入，回调函数则在第一次调用return的防抖函数后延迟指定毫秒调用。
                                    如果`atBegin`为true，回调函数则在第一次调用return的防抖函数时直接执行
 * @param  {Function} callback      延迟毫秒后执行的函数。`this`上下文和所有参数都是按原样传递的，
 *                                  执行去抖动功能时，，调用`callback`。
 *
 * @return {Function} 新的防抖函数。
 */
function debounce(delay, atBegin, callback) {
  return callback === undefined
    ? throttle(delay, atBegin, false)
    : throttle(delay, callback, atBegin !== false);
}

/**
 * @desc   函数节流。
 * 适用于限制`resize`和`scroll`等函数的调用频率
 *
 * @param  {Number}    delay          0 或者更大的毫秒数。 对于事件回调，大约100或250毫秒（或更高）的延迟是最有用的。
 * @param  {Boolean}   noTrailing     可选，默认为false。
 *                                    如果noTrailing为true，当节流函数被调用，每过`delay`毫秒`callback`也将执行一次。
 *                                    如果noTrailing为false或者未传入，`callback`将在最后一次调用节流函数后再执行一次.
 *                                    （延迟`delay`毫秒之后，节流函数没有被调用,内部计数器会复位）
 * @param  {Function}  callback       延迟毫秒后执行的函数。`this`上下文和所有参数都是按原样传递的，
 *                                    执行去节流功能时，调用`callback`。
 * @param  {Boolean}   debounceMode   如果`debounceMode`为true，`clear`在`delay`ms后执行。
 *                                    如果debounceMode是false，`callback`在`delay` ms之后执行。
 *
 * @return {Function}  新的节流函数
 */
function throttle(delay, noTrailing, callback, debounceMode) {
  // After wrapper has stopped being called, this timeout ensures that
  // `callback` is executed at the proper times in `throttle` and `end`
  // debounce modes.
  var timeoutID;

  // Keep track of the last time `callback` was executed.
  var lastExec = 0;

  // `noTrailing` defaults to falsy.
  if (typeof noTrailing !== "boolean") {
    debounceMode = callback;
    callback = noTrailing;
    noTrailing = undefined;
  }

  // The `wrapper` function encapsulates all of the throttling / debouncing
  // functionality and when executed will limit the rate at which `callback`
  // is executed.
  function wrapper() {
    var self = this;
    var elapsed = Number(new Date()) - lastExec;
    var args = arguments;

    // Execute `callback` and update the `lastExec` timestamp.
    function exec() {
      lastExec = Number(new Date());
      callback.apply(self, args);
    }

    // If `debounceMode` is true (at begin) this is used to clear the flag
    // to allow future `callback` executions.
    function clear() {
      timeoutID = undefined;
    }

    if (debounceMode && !timeoutID) {
      // Since `wrapper` is being called for the first time and
      // `debounceMode` is true (at begin), execute `callback`.
      exec();
    }

    // Clear any existing timeout.
    if (timeoutID) {
      clearTimeout(timeoutID);
    }

    if (debounceMode === undefined && elapsed > delay) {
      // In throttle mode, if `delay` time has been exceeded, execute
      // `callback`.
      exec();
    } else if (noTrailing !== true) {
      // In trailing throttle mode, since `delay` time has not been
      // exceeded, schedule `callback` to execute `delay` ms after most
      // recent execution.
      //
      // If `debounceMode` is true (at begin), schedule `clear` to execute
      // after `delay` ms.
      //
      // If `debounceMode` is false (at end), schedule `callback` to
      // execute after `delay` ms.
      timeoutID = setTimeout(
        debounceMode ? clear : exec,
        debounceMode === undefined ? delay - elapsed : delay
      );
    }
  }

  // Return the wrapper function.
  return wrapper;
}
/**
 *
 * @desc 随机生成颜色
 * @return {String}
 */
function randomColor() {
  return (
    "#" + ("00000" + ((Math.random() * 0x1000000) << 0).toString(16)).slice(-6)
  );
}
/**
 *
 * @desc 生成指定范围[min, max]的随机数
 * @param  {Number} min
 * @param  {Number} max
 * @return {Number}
 */
function randomNum(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
 * 图片压缩
 * @param  {[type]}   file [压缩文件]
 * @param  {[type]}   obj  [压缩参数]
 * @param  {Function} cb   [回调函数]
 * @return {[type]}        [返回压缩前和压缩后的格式]
 */
function photoCompress(file, obj, cb) {
  /*
      obj = {
          width: 图片宽,
          height: 图片高,
          quality: 图像质量，
          blob: 是否转换成Blob
      }
    */
  //将以base64的图片url数据转换为Blob
  function convertBase64UrlToBlob(urlData) {
    var arr = urlData.split(","),
      mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]),
      n = bstr.length,
      u8arr = new Uint8Array(n);
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], { type: mime });
  }

  // canvas 绘制图片
  function canvasDataURL(oldBase64) {
    var img = new Image();
    img.src = oldBase64;
    img.onload = function () {
      var that = this;
      // 默认按比例压缩
      var w = that.width,
        h = that.height,
        scale = w / h;
      w = obj.width || w;
      h = obj.height || w / scale;
      var quality = 0.7; // 默认图片质量为0.7
      //生成canvas
      var canvas = document.createElement("canvas");
      var ctx = canvas.getContext("2d");
      // 创建属性节点
      var anw = document.createAttribute("width");
      anw.nodeValue = w;
      var anh = document.createAttribute("height");
      anh.nodeValue = h;
      canvas.setAttributeNode(anw);
      canvas.setAttributeNode(anh);
      ctx.drawImage(that, 0, 0, w, h);
      // 图像质量
      if (obj.quality && obj.quality <= 1 && obj.quality > 0) {
        quality = obj.quality;
      }
      // quality值越小，所绘制出的图像越模糊
      var base64 = canvas.toDataURL("image/jpeg", quality);
      // 回调函数返回base64的值
      if (obj.blob) {
        cb &&
          cb(convertBase64UrlToBlob(base64), convertBase64UrlToBlob(oldBase64));
      } else {
        cb && cb(base64, oldBase64);
      }
    };
  }

  // 读取图片的base64格式
  var ready = new FileReader();
  ready.readAsDataURL(file);
  ready.onload = function () {
    var re = this.result;
    canvasDataURL(re);
  };
}

/**
 * @description: 图片加载
 * @param {*} arr
 * @param {*} callback
 * @return {*}
 */
function imgLoadAll(arr, callback) {
  var arrImg = [];
  for (var i = 0; i < arr.length; i++) {
    var img = new Image();
    img.src = arr[i];
    img.onload = function () {
      arrImg.push(this);
      if (arrImg.length == arr.length) {
        callback && callback();
      }
    };
  }
}

/**
 * @description: 音频加载
 * @param {*} src
 * @param {*} callback
 * @return {*}
 */
function loadAudioloadAudio(src, callback) {
  var audio = new Audio(src);
  audio.onloadedmetadata = callback;
  audio.src = src;
}
/**
 * 光标所在位置插入字符，并设置光标位置 依赖 insertAtCursor
 *
 * @param {dom} 输入框
 * @param {val} 插入的值
 * @param {posLen} 光标位置处在 插入的值的哪个位置
 */
function setCursorPosition(dom, val, posLen) {
  var cursorPosition = 0;
  if (dom.selectionStart) {
    cursorPosition = dom.selectionStart;
  }
  insertAtCursor(dom, val);
  dom.focus();
  console.log(posLen);
  dom.setSelectionRange(
    dom.value.length,
    cursorPosition + (posLen || val.length)
  );
}

/**
 * @description: 光标所在位置插入字符
 * @param {*} dom
 * @param {*} val
 * @return {*}
 */
function insertAtCursor(dom, val) {
  if (document.selection) {
    dom.focus();
    let sel = document.selection.createRange();
    sel.text = val;
    sel.select();
  } else if (dom.selectionStart || dom.selectionStart == "0") {
    let startPos = dom.selectionStart;
    let endPos = dom.selectionEnd;
    let restoreTop = dom.scrollTop;
    dom.value =
      dom.value.substring(0, startPos) +
      val +
      dom.value.substring(endPos, dom.value.length);
    if (restoreTop > 0) {
      dom.scrollTop = restoreTop;
    }
    dom.focus();
    dom.selectionStart = startPos + val.length;
    dom.selectionEnd = startPos + val.length;
  } else {
    dom.value += val;
    dom.focus();
  }
}
/*
    let url = 'http://demo.com/api'
    例: 
        ajax({
            url: url,
            success: function(data) {
    
            }
        })
*/
function ajax(setting) {
  //设置参数的初始值
  var opts = {
    method: (setting.method || "GET").toUpperCase(), //请求方式
    url: setting.url || "", // 请求地址
    async: setting.async || true, // 是否异步
    dataType: setting.dataType || "json", // 解析方式
    data: setting.data || "", // 参数
    success: setting.success || function () {}, // 请求成功回调
    error: setting.error || function () {}, // 请求失败回调
  };

  // 参数格式化
  function params_format(obj) {
    var str = "";
    for (var i in obj) {
      str += i + "=" + obj[i] + "&";
    }
    return str.split("").slice(0, -1).join("");
  }

  // 创建ajax对象
  var xhr = new XMLHttpRequest();

  // 连接服务器open(方法GET/POST，请求地址， 异步传输)
  if (opts.method == "GET") {
    xhr.open(
      opts.method,
      opts.url + "?" + params_format(opts.data),
      opts.async
    );
    xhr.send();
  } else {
    xhr.open(opts.method, opts.url, opts.async);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.send(opts.data);
  }

  /*
   ** 每当readyState改变时，就会触发onreadystatechange事件
   ** readyState属性存储有XMLHttpRequest的状态信息
   ** 0 ：请求未初始化
   ** 1 ：服务器连接已建立
   ** 2 ：请求已接受
   ** 3 : 请求处理中
   ** 4 ：请求已完成，且相应就绪
   */
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 304)) {
      switch (opts.dataType) {
        case "json":
          var json = JSON.parse(xhr.responseText);
          opts.success(json);
          break;
        case "xml":
          opts.success(xhr.responseXML);
          break;
        default:
          opts.success(xhr.responseText);
          break;
      }
    }
  };

  xhr.onerror = function (err) {
    opts.error(err);
  };
}
/**
 * @description: foreach同步执行
 * @param {*} array
 * @param {*} callback
 * @return {*}
 */
async function asyncForEach(array, callback) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array);
  }
}
/**
 * @description: 等待
 * @param {*} milliseconds
 * @return {*}
 */
function pWait(milliseconds) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, milliseconds);
  });
}
/**
 * @description: 复制文本到剪切板
 * @param {*} data
 * @return {*}
 */
function copyToClipboard(data) {
  const _tempInput = document.createElement("input");
  _tempInput.value = data.value;
  document.body.appendChild(_tempInput);
  _tempInput.select();
  document.execCommand("Copy");
  document.body.removeChild(_tempInput);
}
/**
 * @description: 前端生成文件并下载
 * @param {*} fileName
 * @param {*} content new Blob([content])
 * @return {*}
 */
function createAndDownloadFile(fileName, content) {
  const aTag = document.createElement("a");
  const blob = new Blob([content]);
  aTag.download = `${fileName}.json`;
  aTag.href = URL.createObjectURL(blob);
  aTag.click();
  URL.revokeObjectURL(blob);
}
/**
 * @description: 高亮文本
 * @param {*} text
 * @param {*} words
 * @param {*} tag
 * @return {*}
 */
function highlight(text, words, tag = "span") {
  let i,
    len = words.length,
    re;
  for (i = 0; i < len; i++) {
    re = new RegExp(words[i], "g");
    if (re.test(text)) {
      text = text.replace(
        re,
        "<" + tag + ' class="highlight">$&</' + tag + ">"
      );
    }
  }
  return text;
}
/**
 * @description: 限制文本字数
 * @param {*}
 * @return {*}
 */
function excerpt(str, nwords) {
  let words = str.split(" ");
  words.splice(nwords, words.length - 1);
  return words.join(" ") + (words.length !== str.split(" ").length ? "…" : "");
}
/**
 * @description: 返回顶部的通用方法
 * @param {*} selector
 * @return {*}
 */
function backTop(selector) {
  // var btn = document.getElementById(btnId);
  var btn = document.querySelector(selector);
  var d = document.documentElement;
  var b = document.body;
  window.onscroll = set;
  btn.style.display = "none";
  btn.onclick = function () {
    btn.style.display = "none";
    window.onscroll = null;
    this.timer = setInterval(function () {
      d.scrollTop -= Math.ceil((d.scrollTop + b.scrollTop) * 0.1);
      b.scrollTop -= Math.ceil((d.scrollTop + b.scrollTop) * 0.1);
      if (d.scrollTop + b.scrollTop == 0)
        clearInterval(btn.timer, (window.onscroll = set));
    }, 10);
  };
  function set() {
    btn.style.display = d.scrollTop + b.scrollTop > 100 ? "block" : "none";
  }
}
/**
 * @description: 检验URL链接是否有效
 * @param {*} URL
 * @return {*}
 */
function getUrlState(URL) {
  var xmlhttp = new ActiveXObject("microsoft.xmlhttp");
  xmlhttp.Open("GET", URL, false);
  try {
    xmlhttp.Send();
  } catch (e) {
  } finally {
    var result = xmlhttp.responseText;
    if (result) {
      if (xmlhttp.Status == 200) {
        return true;
      } else {
        return false;
      }
    } else {
      return false;
    }
  }
}
/**
 * @description: 动态加载js或css文件
 * @param {*} url
 * @return {*}
 */
function delay_js(url) {
  var type = url.split("."),
    file = type[type.length - 1];
  if (file == "css") {
    var obj = document.createElement("link"),
      lnk = "href",
      tp = "text/css";
    obj.setAttribute("rel", "stylesheet");
  } else
    var obj = document.createElement("script"),
      lnk = "src",
      tp = "text/javascript";
  obj.setAttribute(lnk, url);
  obj.setAttribute("type", tp);
  file == "css"
    ? document.getElementsByTagName("head")[0].appendChild(obj)
    : document.body.appendChild(obj);
  return obj;
}
/**
 * @description: 从全局捕获错误 https://www.jianshu.com/p/315ffe6797b8
 * @param {*} cb
 * @return {*}
 */
function onError(cb) {
  // 从全局捕获错误
  window.onerror = function (
    errMsg,
    scriptURI,
    lineNumber,
    columnNumber,
    errorObj
  ) {
    cb({errMsg, scriptURI, lineNumber, columnNumber, errorObj});
  };
}
/**
 * @description: ready实现 document.ready(cb)
 * @param {*} callback
 * @return {*}
 */
function ready(callback) {
  ///兼容FF,Google
  if (document.addEventListener) {
    document.addEventListener(
      "DOMContentLoaded",
      function () {
        document.removeEventListener(
          "DOMContentLoaded",
          arguments.callee,
          false
        );
        callback();
      },
      false
    );
  }
  //兼容IE
  else if (document.attachEvent) {
    document.attachEvent("onreadystatechange", function () {
      if (document.readyState == "complete") {
        document.detachEvent("onreadystatechange", arguments.callee);
        callback();
      }
    });
  } else if (document.lastChild == document.body) {
    callback();
  }
};
export default {
  debounce,
  throttle,
  randomColor,
  randomNum,
  photoCompress,
  imgLoadAll,
  loadAudioloadAudio,
  setCursorPosition,
  insertAtCursor,
  ajax,
  asyncForEach,
  pWait,
  copyToClipboard,
  createAndDownloadFile,
  highlight,
  excerpt,
  backTop,
  getUrlState,
  delay_js,
  onError,
  ready,
};
